/*
 * FreeSSM.cpp - Program main window
 *
 * Copyright (C) 2008-2010 Comer352l
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "FreeSSM.h"
#include "ui_FreeSSM.h"


FreeSSM::FreeSSM(QWidget *parent):
    QWidget(parent),
    ui(new Ui::FreeSSM)
{
        _qt_translator = NULL;
	_translator = NULL;
	_iface_type = AbstractDiagInterface::interface_serialPassThrough;
	_iface_filename = "";
	_language = "en";	// default language
	_dumping = false;
        QString appsPath( QDir::homePath() );
	// SETUP GUI:
        ui->setupUi(this);
        errmessage = new myMessage();
	// LOAD PREFERENCES FROM FILE:
	QString savedinterfacefilename = "";
	QString savedlanguage = "";
	QString savedinterfacetype = "";
        settings = new QSettings("carpc", "freessm", this);
        settings->setPath(QSettings::IniFormat,QSettings::UserScope,"carpc");
        savedinterfacefilename =  settings->value("interface").toString();
        savedinterfacetype = settings->value("interfacetype").toString();
        savedlanguage = settings->value("language").toString();
	// CHECK SAVED LANGUAGE:
	bool sl_valid = false;
	QLocale loc = QLocale(savedlanguage);
	if ((loc != QLocale::C) && (__supportedLocales.indexOf( loc ) > -1))
	{
		_language = savedlanguage;
		sl_valid = true;
	}
	// TRY TO SELECT SYSTEM LANGUAGE, IF SAVED LANGUAGE IS INVALID:
	if (!sl_valid)
	{
		if (__supportedLocales.indexOf( QLocale::system() ) > -1)
			_language = QLocale::system().name().section('_', 0, 0);
	}
	// SET TRANSLATOR AND RETRANSLATE:
	_translator = new QTranslator;
	bool langfileerror = false;
	langfileerror = !_translator->load("FreeSSM_" + _language + ".qm", appsPath);
	if (langfileerror && (_language != "en"))
	{
		// Fallback to English
		_language = "en";
		langfileerror = !_translator->load("FreeSSM_en.qm", appsPath);
		// Display error message about missing language file:
                myMessage message(0);
                message.exec(2,tr("Error:\n- Language file missing or damaged -"));
	}
	if (!langfileerror)
	{
                //installTranslator(_translator);
                //retranslateUi(this);
	}
	else
	{
		delete _translator;
		_translator = NULL;
	}
	// SET Qt-TRANSLATOR (if necessary and available):
	if (_language != "en")
	{
		QString qt_ts_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
		if (qt_ts_path.isEmpty())
			qt_ts_path = QCoreApplication::applicationDirPath();
		_qt_translator = new QTranslator;
                if (_qt_translator->load("qt_" + _language, qt_ts_path)) {
                        //app->installTranslator(_qt_translator);
                } else {
			delete _qt_translator;
			_qt_translator = NULL;
		}
	}
	// CHECK THE SAVED INTERFACE SETTINGS AND CORRECT IF NECESSARY:
	else if (savedinterfacetype == QString::number(AbstractDiagInterface::interface_J2534))	// J2534-Pass-Through
	{
		_iface_type = AbstractDiagInterface::interface_J2534;
		std::vector<J2534Library> J2534libs = J2534_API::getAvailableJ2534Libs();
		if (J2534libs.size())
		{
			for (unsigned int k=0; k<J2534libs.size(); k++)
			{
				if (savedinterfacefilename == QString::fromStdString(J2534libs.at(k).path))
				{
					_iface_filename = savedinterfacefilename;
					break;
				}
				if (_iface_filename.isEmpty() && (J2534libs.size() > 0))
					_iface_filename = QString::fromStdString(J2534libs.at(0).path);
				// NOTE: otherwise _iface_filename remains empty
			}
		}
	}
	if (_iface_filename.isEmpty() || savedinterfacetype == QString::number(AbstractDiagInterface::interface_serialPassThrough))	// Serial Pass-Through
	{
		_iface_type = AbstractDiagInterface::interface_serialPassThrough;
		std::vector<std::string> portlist;
		portlist = serialCOM::GetAvailablePorts();
		if (portlist.size())
		{
			for (unsigned int k=0; k<portlist.size(); k++)
			{
				if (savedinterfacefilename == QString::fromStdString(portlist.at(k)))
				{
					_iface_filename = savedinterfacefilename;
					break;
				}
				if (_iface_filename.isEmpty() && (portlist.size() > 0))
					_iface_filename = QString::fromStdString(portlist.at(0));
				// NOTE: otherwise _iface_filename remains empty
			}
		}
	}
	// CREATE ACTION FOR DUMPING CONTROL UNIT ID-DATA TO FILE:
	_dump_action = new QAction(this);
	_dump_action->setShortcut( QKeySequence("Ctrl+Alt+Return") );
	this->addAction(_dump_action);
	// NOTE: using released() instead of pressed() as workaround for a Qt-Bug occuring under MS Windows
	connect( _dump_action, SIGNAL(triggered()), this, SLOT(dumpCUdata()) );
}


FreeSSM::~FreeSSM()
{
	disconnect( _dump_action, SIGNAL( triggered() ), this, SLOT( dumpCUdata() ) );
	delete _dump_action;
	if (_translator != NULL)
	{
		QApplication::removeTranslator(_translator);
		delete _translator;
	}
	if (_qt_translator != NULL)
	{
		QApplication::removeTranslator(_qt_translator);
		delete _qt_translator;
	}
        delete ui;
}

void FreeSSM::on_exit_pushButton_released()
{
    this->~FreeSSM();
}


void FreeSSM::on_engine_pushButton_released()
{
	if (_dumping) return;
	AbstractDiagInterface *diagInterface = initInterface();
	if (diagInterface)
	{
            ui->centralFrame->hide();
            EngineDialog *enginedialog = new EngineDialog(this,diagInterface, _language);
            if (!enginedialog->isHidden())
                enginedialog->exec();
            ui->centralFrame->show();
            delete enginedialog;
            delete diagInterface;
	}
}


void FreeSSM::on_transmission_pushButton_released()
{
	if (_dumping) return;
	AbstractDiagInterface *diagInterface = initInterface();
	if (diagInterface)
	{
            ui->centralFrame->hide();
            TransmissionDialog *transmissiondialog = new TransmissionDialog(this,diagInterface, _language);
            if (!transmissiondialog->isHidden())
                transmissiondialog->exec();
            ui->centralFrame->show();
            delete transmissiondialog;
            delete diagInterface;
	}
}

void FreeSSM::on_about_pushButton_released()
{
    ui->centralFrame->hide();
    About *aboutdlg = new About(this, _language);
	aboutdlg->show();
	aboutdlg->exec();
        ui->centralFrame->show();
        delete aboutdlg;
}


AbstractDiagInterface * FreeSSM::initInterface()
{
	// Check if an interface is selected:
	if (_iface_filename == "")
	{
                errmessage->exec(0,tr("No interface selected !\n=> Please select a dignostic interface in the preferences."));
		return NULL;
	}
	// Open interface:
	AbstractDiagInterface *diagInterface = NULL;
	if (_iface_type == AbstractDiagInterface::interface_serialPassThrough)
	{
		diagInterface = new SerialPassThroughDiagInterface;
	}
	else if (_iface_type == AbstractDiagInterface::interface_J2534)
	{
		diagInterface = new J2534DiagInterface;
	}
	else
	{
                errmessage->exec(0,tr("Internal error:\nThe selected interface type cannot be initialized !\n=> Please report this as a bug."));
		return NULL;
	}
	if (diagInterface->open(_iface_filename.toStdString()))
		return diagInterface;
	// Return error:
        errmessage->exec(0,tr("Couldn't open the diagnostic interface !\nMaybe the device is already in use by another application..."));
	delete diagInterface;
	return NULL;
}


void FreeSSM::retranslate(QString newlanguage, QTranslator *newtranslator)
{
	// Uninstall and delete current translator:
	if (_translator != NULL)
	{ 
		QApplication::removeTranslator(_translator);
		delete _translator; 
		_translator = NULL;
	}
	// Save new language settings:
	_language = newlanguage;
	_translator = newtranslator;
	// Install new translator and retranslate:
	QApplication::installTranslator(_translator);
        //this->retranslateUi(this);
	// Change translator for Qt-dialogs:
	if (_qt_translator != NULL)
	{
		QApplication::removeTranslator(_qt_translator);
		delete _qt_translator;
		_qt_translator = NULL;
	}
	if (newlanguage != "en")
	{
		QString qt_ts_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
		if (qt_ts_path.isEmpty())
			qt_ts_path = QCoreApplication::applicationDirPath();
		_qt_translator = new QTranslator;
		if (_qt_translator->load("qt_" + newlanguage, qt_ts_path))
			QApplication::installTranslator(_qt_translator);
		else
		{
			delete _qt_translator;
			_qt_translator = NULL;
		}
	}
}


void FreeSSM::dumpCUdata()
{
	QFile dumpfile;
	char SYS_ID[3] = {0};
	char ROM_ID[5] = {0};
	char flagbytes[96] = {0};
	unsigned char nrofflagbytes = 0;
	std::string hexstr = "";
	unsigned char k = 0;
	unsigned int dataaddr[17] = {0};
	char data[17] = {0};
	int ssm1_cu_index = SSM1_CU_Engine;

	if (_dumping) return;
	// Initialize and configure serial port:
	AbstractDiagInterface *diagInterface = initInterface();
	if (diagInterface)
	{
		if (!diagInterface->connect(AbstractDiagInterface::protocol_SSM2))
		{
			delete diagInterface;
			return;
		}
	}
	else
		return;
	_dumping = true;
	// Set filename:
	QString filename(QDir::homePath() + "/dump");
	if (QFile::exists(filename + ".dat"))
	{
		for (unsigned int f=2; f<65535; f++)
		{
			if (!QFile::exists(filename + QString::number(f) + ".dat"))
			{
				filename.append(QString::number(f));
				break;
			}
		}
	}
	filename.append(".dat");
	dumpfile.setFileName(filename);
	// Create SSMP1-Communication-object:
	SSMP1communication SSMP1com(diagInterface, SSM1_CU_Engine, 0);
	// Create SSMP2-Communication-object:
	SSMP2communication SSMP2com(diagInterface, '\x10', 0);
	// ######## SSM2-Control-Units ########
	// **************** ECU ***************
	// Read ECU data:
	if (SSMP2com.getCUdata(SYS_ID, ROM_ID, flagbytes, &nrofflagbytes))
	{
		// Open/Create File:
		if (!dumpfile.open(QIODevice::WriteOnly | QIODevice::Text))
			goto end;
		// *** Convert and write data to file:
		// Sys-ID:
		hexstr = libFSSM::StrToHexstr(SYS_ID, 3);
		hexstr.push_back('\n');
		dumpfile.write(hexstr.data(), hexstr.length());
		// ROM-ID:
		hexstr = libFSSM::StrToHexstr(ROM_ID, 5);
		hexstr.push_back('\n');
		dumpfile.write(hexstr.data(), hexstr.length());
		// Flagbytes:
		for (k=1; k<=ceil(static_cast<float>(nrofflagbytes)/16); k++)
		{
			hexstr.clear();
			if (16*k <= nrofflagbytes)
				hexstr = libFSSM::StrToHexstr(flagbytes+((k-1)*16), 16);
			else
				hexstr = libFSSM::StrToHexstr(flagbytes+((k-1)*16), (nrofflagbytes%16));
			hexstr.push_back('\n');
			dumpfile.write(hexstr.data(), hexstr.length());
		}
		// VIN:
		if (flagbytes[36] == (flagbytes[36] | 0x01))
		{
			dataaddr[0] = 0xDA;
			dataaddr[1] = 0xDB;
			dataaddr[2] = 0xDC;
			if (!SSMP2com.readMultipleDatabytes(0x0, dataaddr, 3, data))
				goto end;
			hexstr = libFSSM::StrToHexstr(data, 3);
			hexstr.insert(0, "\n");
			hexstr.push_back('\n');
			dumpfile.write(hexstr.data(), hexstr.length());
			dataaddr[0] = (65536 * static_cast<unsigned char>(data[0]))
					+ (256 * static_cast<unsigned char>(data[1]))
					+ (static_cast<unsigned char>(data[2]));
			for (k=1; k<17; k++)
				dataaddr[k] = dataaddr[0]+k;
			if (!SSMP2com.readMultipleDatabytes(0x0, dataaddr, 17, data))
				goto end;
			hexstr = libFSSM::StrToHexstr(data, 17);
			hexstr.push_back('\n');
			dumpfile.write(hexstr.data(), hexstr.size());
		}
	}
	else if (dumpfile.isOpen())
		dumpfile.write("\n---\n", 5);
	// **************** TCU ***************
	SSMP2com.setCUaddress('\x18');
	// Read TCU data:
	if (SSMP2com.getCUdata(SYS_ID, ROM_ID, flagbytes, &nrofflagbytes))
	{
		if (!dumpfile.isOpen()) // if file is not opened yet
		{
			// Open/Create File:
			if (!dumpfile.open(QIODevice::WriteOnly | QIODevice::Text))
				goto end;
			dumpfile.write("\n---\n", 5);
		}
		// *** Convert and write data to file:
		// Sys-ID:
		hexstr = libFSSM::StrToHexstr(SYS_ID, 3);
		hexstr.insert(0, "\n");
		hexstr.push_back('\n');
		dumpfile.write(hexstr.data(), hexstr.length());
		// ROM-ID:
		hexstr = libFSSM::StrToHexstr(ROM_ID, 5);
		hexstr.push_back('\n');
		dumpfile.write(hexstr.data(), hexstr.length());
		// Flagbytes:
		for (k=1; k<=ceil(static_cast<float>(nrofflagbytes)/16); k++)
		{
			hexstr.clear();
			if (16*k <= nrofflagbytes)
				hexstr = libFSSM::StrToHexstr(flagbytes+((k-1)*16), 16);
			else
				hexstr = libFSSM::StrToHexstr(flagbytes+((k-1)*16), (nrofflagbytes%16));
			hexstr.push_back('\n');
			dumpfile.write(hexstr.data(), hexstr.length());
		}
	}
	else if (dumpfile.isOpen())
		dumpfile.write("\n---\n", 5);
	diagInterface->disconnect();
	// ######## SSM1-Control-Units ########
	// Configure interface:
	if (!diagInterface->connect(AbstractDiagInterface::protocol_SSM1))
		goto end;
	// Dump all SSM1-CUs:
	for (ssm1_cu_index=SSM1_CU_Engine; ssm1_cu_index<=SSM1_CU_PwrSteer; ssm1_cu_index++)
	{
		// Select CU:
		SSMP1com.selectCU( SSM1_CUtype_dt(ssm1_cu_index) );
		// Read CU-ID(s):
		if (SSMP1com.getCUdata(SYS_ID, flagbytes, &nrofflagbytes))
		{
			if (!dumpfile.isOpen())
			{
				if (!dumpfile.open(QIODevice::WriteOnly | QIODevice::Text))
					goto end;
				for (k=0; k<(ssm1_cu_index+2); k++)
					dumpfile.write("\n-----\n", 7);
			}
			if (((SYS_ID[0] & '\xF0') == '\xA0') && (SYS_ID[1] == '\x10'))
			{
				// Read extended ID:
				std::vector<unsigned int> addresses;
				for (unsigned int addr=0x01; addr<=0x05; addr++)
					addresses.push_back(addr);
				std::vector<char> data;
				if (!SSMP1com.readAddresses(addresses, &data))
					goto end;
				for (unsigned char i=0; i<5; i++)
					ROM_ID[i] = data.at(i);
			}
			// *** Convert and write data to file:
			// ID:
			hexstr = libFSSM::StrToHexstr(SYS_ID, 3);
			hexstr.insert(0, "\n");
			hexstr.push_back('\n');
			dumpfile.write(hexstr.data(), hexstr.length());
			// Etended ID:
			if (((SYS_ID[0] & '\xF0') == '\xA0') && (SYS_ID[1] == '\x10'))
			{
				hexstr = libFSSM::StrToHexstr(ROM_ID, 5);
				hexstr.push_back('\n');
				dumpfile.write(hexstr.data(), hexstr.length());
			}
			else
				dumpfile.write("---\n", 4);
			// Flagbytes:
			if (nrofflagbytes)
			{
				hexstr = libFSSM::StrToHexstr(flagbytes, nrofflagbytes);
				hexstr.push_back('\n');
				dumpfile.write(hexstr.data(), hexstr.length());
			}
			else
				dumpfile.write("---\n", 4);
		}
		else if (dumpfile.isOpen())
			dumpfile.write("\n-----\n", 7);
	}
	diagInterface->disconnect();

end:
	dumpfile.close();
	delete diagInterface;	// will be closed in destructor
	_dumping = false;
}

void FreeSSM::closeEvent(QCloseEvent *event)
{
	if (!_dumping)
		event->accept();
	else
		event->ignore();
}



